{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b473d98c",
   "metadata": {},
   "source": [
    "## 传递数据\n",
    "\n",
    "RunnablePassthrough 允许不改变或添加额外的键来传递输入。这通常与 RunnableParallel 结合使用，将数据分配给映射中的新键。\n",
    "\n",
    "RunnablePassthrough() 单独调用，将简单地获取输入并将其传递。\n",
    "\n",
    "使用分配 ( RunnablePassthrough.assign(...) ) 调用的 RunnablePassthrough 将获取输入，并将添加传递给分配函数的额外参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "dd84464e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'passed': {'num': 1}, 'extra': {'num': 1, 'mult': 3}, 'modified': 2}"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "\n",
    "runnable = RunnableParallel(\n",
    "    passed=RunnablePassthrough(),\n",
    "    extra=RunnablePassthrough.assign(mult=lambda x: x[\"num\"] * 3),\n",
    "    modified=lambda x: x[\"num\"] + 1,\n",
    ")\n",
    "\n",
    "runnable.invoke({\"num\": 1})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2bd356cc",
   "metadata": {},
   "source": [
    "如上所示， passed 键是用 RunnablePassthrough() 调用的，因此它只是传递 {'num': 1} 。\n",
    "\n",
    "在第二行中，我们使用 RunnablePastshrough.assign 和一个将数值乘以 3 的 lambda。在这种情况下， extra 设置为 {'num': 1, 'mult': 3} ，这是原始的添加了 mult 键的值。\n",
    "\n",
    "最后，我们还在映射中使用 modified 设置了第三个键，它使用 lambda 来设置单个值，在 num 上加 1，从而得到 modified 键，其值为 < b2> 。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95589e7e",
   "metadata": {},
   "source": [
    "## 运行自定义函数\n",
    "\n",
    "您可以在管道中使用任意函数。\n",
    "\n",
    "请注意，这些函数的所有输入都必须是单个参数。如果您有一个接受多个参数的函数，则应该编写一个接受单个输入并将其解包为多个参数的包装器。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e7443a49",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI, OpenAI\n",
    "openai_api_key = \"EMPTY\"\n",
    "openai_api_base = \"http://127.0.0.1:1234/v1\"\n",
    "model = ChatOpenAI(\n",
    "    openai_api_key=openai_api_key,\n",
    "    openai_api_base=openai_api_base,\n",
    "    temperature=0.3,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "e5a0f074",
   "metadata": {},
   "outputs": [],
   "source": [
    "from operator import itemgetter\n",
    "\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.runnables import RunnableLambda\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "\n",
    "def length_function(text):\n",
    "    return len(text)\n",
    "\n",
    "\n",
    "def _multiple_length_function(text1, text2):\n",
    "    return len(text1) * len(text2)\n",
    "\n",
    "\n",
    "def multiple_length_function(_dict):\n",
    "    return _multiple_length_function(_dict[\"text1\"], _dict[\"text2\"])\n",
    "\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"what is {a} + {b}\")\n",
    "\n",
    "\n",
    "chain1 = prompt | model\n",
    "\n",
    "chain = (\n",
    "    {\n",
    "        \"a\": itemgetter(\"foo\") | RunnableLambda(length_function),\n",
    "        \"b\": {\"text1\": itemgetter(\"foo\"), \"text2\": itemgetter(\"bar\")} | RunnableLambda(multiple_length_function),\n",
    "    }\n",
    "    | prompt\n",
    "    | model\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e87af8cf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='To find the sum of 3 and 9, we can simply add them together.\\n\\n3 + 9 = 12')"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"bar\", \"bar\": \"gah\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07154882",
   "metadata": {},
   "source": [
    "## 根据输入动态路由逻辑\n",
    "\n",
    "路由允许您创建非确定性链，其中上一步的输出定义下一步。路由有助于提供与 LLMs 交互的结构和一致性。\n",
    "\n",
    "执行路由的方法有两种：\n",
    "\n",
    "1. 有条件地从 RunnableLambda 返回可运行对象（推荐）\n",
    "2. 使用 RunnableBranch "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "64ce47bd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'OpenAI'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "\n",
    "prompt = PromptTemplate.from_template(\n",
    "        \"\"\"鉴于下面的用户问题，将其分类为“LangChain”、“OpenAI”或“其他”。\n",
    "\n",
    "不要用超过一个字来回应。\n",
    "\n",
    "<question>\n",
    "{question}\n",
    "</question>\n",
    "\n",
    "分类：\"\"\"\n",
    ")\n",
    "\n",
    "chain = (\n",
    "    prompt\n",
    "    | model\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "chain.invoke({\"question\": \"how do I call OpenAI?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "16b2224f",
   "metadata": {},
   "outputs": [],
   "source": [
    "langchainPrompt = PromptTemplate.from_template(\n",
    "        \"\"\"您是 langchain 方面的专家。 \n",
    "回答问题时始终以“正如老陈告诉我的那样”开头。 \n",
    "回答以下问题：\n",
    "\n",
    "问题：{question}\n",
    "回答：\"\"\"\n",
    ")\n",
    "langchain_chain = langchainPrompt | model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9c068625",
   "metadata": {},
   "outputs": [],
   "source": [
    "OpenAIPrompt = PromptTemplate.from_template(\n",
    "        \"\"\"您是 OpenAI 方面的专家。 \n",
    "回答问题时始终以“正如奥特曼告诉我的那样”开头。 \n",
    "回答以下问题：\n",
    "\n",
    "问题：{question}\n",
    "回答：\"\"\"\n",
    ")\n",
    "OpenAI_chain = OpenAIPrompt | model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "5c75f1d2",
   "metadata": {},
   "outputs": [],
   "source": [
    "generalPrompt = PromptTemplate.from_template(\n",
    "        \"\"\" 回答以下问题：\n",
    "\n",
    "问题：{question}\n",
    "回答：\"\"\"\n",
    ")\n",
    "general_chain = generalPrompt | model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "55b590c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def route(info):\n",
    "    if \"OpenAI\" in info[\"topic\"]:\n",
    "        return OpenAI_chain\n",
    "    elif \"LangChain\" in info[\"topic\"]:\n",
    "        return langchain_chain\n",
    "    else:\n",
    "        return general_chain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "0d3fa038",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnableLambda\n",
    "\n",
    "full_chain = {\"topic\": chain, \"question\": lambda x: x[\"question\"]} | RunnableLambda(route)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "85918dac",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='正如奥特曼告诉我的一样，要使用OpenAI的模型，您需要遵循以下几个步骤：\\n1. 注册一个OpenAI账户并获得API密钥。这可以通过访问OpenAI官网（https://openai.com/api）来完成。\\n2. 选择适合您需求的模型。OpenAI提供了多种语言生成和文本理解模型，包括GPT-3、Davinci、Curie、Babbage等。根据您的应用场景和技术要求，选择最适合的模型。\\n3. 使用API密钥访问OpenAI API。您可以使用各种编程语言（如Python、JavaScript）或第三方库来调用OpenAI API。在代码中包含API密钥，并按照API文档中的示例进行操作。\\n4. 准备输入数据和参数。根据所选的模型和应用场景，准备好相应的文本或其他形式的数据作为模型的输入。同时，确保您了解该模型所需的参数设置，以便获得最佳效果。\\n5. 调用API并获取输出结果。将准备好的输入数据发送到OpenAI API，等待响应并解析返回的结果。根据模型输出的内容，您可以将其用于您的应用场景中。\\n6. 调整和优化模型性能。在开始使用OpenAI的模型后，您可能需要根据实际效果进行调整。这可能包括更改参数设置、重新训练模型或选择不同的模型等。')"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "full_chain.invoke({\"question\": \"我如何使用OpenAI的模型?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d2d68e3c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
